1. 如何理解原型和原型链
js 的所有对象都继承自原型对象,每个对象上都有一个私有属性指向另一个原型对象,原型对象上也有一个自己的原型,依此类推,直到一个对象的原型是 null,根据定义 null 没有原型,所以它就是这个原型链的最后一个节点。
2. [] 的原型链是怎样的?
[] > Array > Object > null
3. __proto__ [[Prototype]] prototype 区别是什么
js 中,每个物件都有一个内部属性 [[Prototype]] 来标识物件的原型,但是这个内部属性是不能直接访问到的,浏览器就实现了非标准的访问方法 __proto__, 实际开发中建议通过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 函数来访问。
prototype 表示的是函数 func.prototype, 是存在于构造函数中的一个属性,构造函数的 prototype 和 __proto__ 指向同一个原型对象。
1 2 3 4 5 6 7 8 9
   | function Person() {} let personA = new Person();
  console.log(Person.prototype.constructor.name); 
 
  personA.__proto__ === Person.prototype;  Object.getPrototypeOf(personA) === Person.prototype;  personA.__proto__ === Object.getPrototypeOf(personA); 
  | 
 
4. js 继承有哪几种方式
1. 原型链继承
1 2 3 4 5 6 7 8 9 10 11 12
   | function Animal() {}
 
  const cat = new Animal();
 
  Animal.prototype.sleep = function () {   console.log('sleep'); };
 
  cat.sleep(); 
  | 
 
主要问题:
- 引用类型的属性被所有实例共享
 
- 在创建 cat 实例的时候,无法向 Animal 构造函数中传参
 
2.构造函数继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | function Super() {   this.colors = ['green', 'blue', 'red']; }
  function Sub() {   Super.call(this); }
  let sub1 = new Sub(); sub1.colors.push('yellow');  console.log(sub1.colors); 
  let sub2 = new Sub(); console.log(sub2.colors); 
  | 
 
解决了原型继承中实例共享和无法向父构造函数传参的问题,但是方法都定义在构造函数中,每次创建实例都会创建一次所有方法。
3. 组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | function Super(name, age) {   this.name = name || 'xiaoming';   this.age = age || 30;  }
  function Sub(name, age) {   Super.call(this, name, age);  }
  Super.prototype.hello = function() {   return `${this.name} has ${this.age}`; }
  Sub.prototype = new Super();   Sub.prototype.constructor = Sub; let instance = new Sub('xiaohong', 18); instance.hello(); 
  | 
 
组合继承结合了原型链和构造函数继承的优点,函数可复用,可向父构造函数传参,实例属性不共享,但是缺点是父构造函数调用了两次。
4. 寄生组合式继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | function Super(name, age) {   this.name = name || 'xiaoming';   this.age = age || 30;  }
  function Sub(name, age) {   Super.call(this, name, age);  }
  Super.prototype.hello = function() {   return `${this.name} has ${this.age}`; }
  Sub.prototype = Object.create(Super.prototype);  Sub.prototype.constructor = Sub;
  Sub.prototype.getName = function() {   return this.name; }
  let instance = new Sub('xiaohong', 18); instance.hello();  instance.getName(); 
  | 
 
该继承方式弥补了组合式继承调用两次父类的问题,也就是我们常用的 es5 继承方法。
5.类继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | class People {   constructor(name, age) {     this.name = name;     this.age = age;   }
    hello() {     console.log(`${this.name} has ${this.age}`);   } }
  class Woman extends People {   constructor(name, age) {     super(name, age);   }
    hello() {     super.hello();   } }
  let girl = new Woman('lili', 18); girl.hello(); 
  | 
 
class 实际上就是 es5 构造函数的语法糖,通过 new 关键字创建实例,通过 extends 实现继承,extends 的作用就是将 Woman.prototype 指向 People. extends 的实现逻辑其实就是来自于寄生组合式继承方法。
es6 通过 babel 编译成 es5,其中实现继承的逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | function _inherits(subClass, superClass) {      if (typeof superClass !== "function" && superClass !== null) {          throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);      }      subClass.prototype = Object.create(superClass && superClass.prototype, {          constructor: {              value: subClass,              enumerable: false,              writable: true,              configurable: true          }      });      if (superClass)          Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;  }
  |